﻿
Imports System.Net
Imports std
Imports occommon

Public Class ocsitecontext
    Public Const CONNECTION_ERROR_STATUSCODE As Int32 = Net.HttpStatusCode.BadGateway
    Public Const CONNECTION_ERROR_DESCRIPTION As String = "CANNOT CONNECT TO ONLINECOOKIE SERVER"
    Public Const CONTENT_ERROR_STATUSCODE As Int32 = Net.HttpStatusCode.HttpVersionNotSupported
    Public Const CONTENT_ERROR_DESCRIPTION As String = "ONLINECOOKIE SERVER RESPONDED INVALID CONTENT"
    Public Const cookieNameValueSeparator As Char = network.cookieNameValueSeparator
    Public Shared timeoutMS As Int64 = 0
    Private Shared OCCONTEXT_SITEGET_TIMEMS As Int64 = 0
    Private Shared OCCONTEXT_SITESET_TIMEMS As Int64 = 0
    Private _serverUrl As String = Nothing
    Private _site As String = Nothing
    Private _userSiteRandomKey As String = Nothing
    Private _user As String = Nothing
    Private _statusCode As Int32 = 0
    Private _description As String = Nothing
    Private _sharedCookies As map(Of String, String) = Nothing
    Private _siteCookies As map(Of String, String) = Nothing

    Shared Sub New()
        assert(Not strstartwith(path.siteGet, network.pathSeperator))
        assert(Not strstartwith(path.siteSet, network.pathSeperator))
        assert(CONNECTION_ERROR_STATUSCODE <> response.statusCode.success)
        assert(CONTENT_ERROR_STATUSCODE <> response.statusCode.success)
        OCCONTEXT_SITEGET_TIMEMS = counter.registerAverageAndLastAverage("OCCONTEXT_SITEGET_TIMEMS")
        OCCONTEXT_SITESET_TIMEMS = counter.registerAverageAndLastAverage("OCCONTEXT_SITESET_TIMEMS")
        timeoutMS = secondsToMilliseconds(10)
    End Sub

    Public Function result() As Boolean
        Return _statusCode = response.statusCode.success
    End Function

    Public Function statusCode() As Int32
        Return _statusCode
    End Function

    Public Function success() As Boolean
        Return _statusCode = response.statusCode.success
    End Function

    Public Function description() As String
        Return copy(_description)
    End Function

    Public Function sharedCookies() As map(Of String, String)
        Return _sharedCookies
    End Function

    Public Function sharedCookie(ByVal name As String) As String
        Return _sharedCookies(name)
    End Function

    Public Function siteCookies() As map(Of String, String)
        Return _siteCookies
    End Function

    Public Function siteCookie(ByVal name As String) As String
        Return _siteCookies(name)
    End Function

    Private Sub setConnectionError()
        _statusCode = CONNECTION_ERROR_STATUSCODE
        _description = CONNECTION_ERROR_DESCRIPTION
    End Sub

    Private Sub setContentError()
        _statusCode = CONTENT_ERROR_STATUSCODE
        _description = CONTENT_ERROR_DESCRIPTION
    End Sub

    Private Function getUrl() As String
        Return urlUtils.appendQuery(_serverUrl + path.siteGet, _
                                    query.site, _site, _
                                    query.user, _user, _
                                    query.userSiteRandomKey, _userSiteRandomKey)
    End Function

    Private Function parseCookie(ByVal cookie As String, ByVal c As map(Of String, String)) As Boolean
        Dim f As String = Nothing
        Dim s As String = Nothing
        If strseg(cookie, f, s, cookieNameValueSeparator) Then
            c(f) = s
        Else
            c(emptyString) = cookie
        End If

        Return True
    End Function

    Private Function parseCookies(ByVal line As String, ByVal c As map(Of String, String)) As Boolean
        Dim v As vector(Of String) = Nothing
        strsepar(line, cookieSeparator, v)
        For i As Int64 = 0 To v.size() - 1
            If Not parseCookie(v(i), c) Then
                raiseError(errorHandle.errorType.warning, "cannot parse cookie ", v(i))
                Return False
            End If
        Next

        Return True
    End Function

    Private Function parseGetContentLine(ByVal line As String) As Boolean
        Dim f As String = Nothing
        Dim s As String = Nothing
        If strseg(line, f, s, contentNameValueSeparator) AndAlso Not isemptystring(f) AndAlso Not isemptystring(s) Then
            If strsame(f, contentNaming.sharedCookies) Then
                Return parseCookies(s, _sharedCookies)
            ElseIf strsame(f, contentNaming.siteCookies) Then
                Return parseCookies(s, _siteCookies)
            Else
                raiseError(errorHandle.errorType.warning, _
                           "onlinecookie server responded unknown content ", f, s, ", ignore.")
                Return True
            End If
        Else
            Return False
        End If
    End Function

    Private Function parseGetContent(ByVal content As String) As Boolean
        Dim v As vector(Of String) = Nothing
        strsepar(content, contentSeparator, v, True)
        For i As Int64 = 0 To v.size() - 1
            If Not parseGetContentLine(v(i)) Then
                Return False
            End If
        Next

        Return True
    End Function

    Public Function [get]() As Boolean
        Dim startticks As Int64 = 0
        startticks = Now.Ticks()
        Dim resp As HttpWebResponse = Nothing
        If requestHttpWeb(getUrl(), resp, timeoutMS:=timeoutMS) Then
            _statusCode = resp.StatusCode
            _description = resp.StatusDescription
            If success() Then
                Dim content As String = Nothing
                If readHttpResponse(resp, content, getUrl()) AndAlso Not isemptystring(content) Then
                    If Not parseGetContent(content) Then
                        raiseError(errorHandle.errorType.warning, _
                                   "onlinecookie server responded success with content, " + _
                                   "but ocsitecontext cannot parse it.")
                        setContentError()
                    End If
                Else
                    raiseError(errorHandle.errorType.warning, _
                               "onlinecookie server responded success status but without content.")
                    setContentError()
                End If
            Else
                resp.Close()
                Select Case _statusCode
                End Select
            End If
        Else
            raiseError(errorHandle.errorType.warning, "cannot connect to onlinecookie server, full url ", getUrl())
            setConnectionError()
        End If
        counter.increase(OCCONTEXT_SITEGET_TIMEMS, ticksToMilliseconds(Now.Ticks() - startticks))

        Return success()
    End Function

    Private Function setUrl() As String
        Return urlUtils.appendQuery(_serverUrl + path.siteSet, _
                                    query.site, _site, _
                                    query.user, _user, _
                                    query.userSiteRandomKey, _userSiteRandomKey)
    End Function

    Private Function setContent() As String
        Dim rtn As String = Nothing
        Dim it As map(Of String, String).iterator = Nothing
        it = _siteCookies.begin()
        While it <> _siteCookies.end()
            rtn += (+it).first() + cookieNameValueSeparator + (+it).second + cookieSeparator
            it += 1
        End While

        Return rtn
    End Function

    Public Function [set]() As Boolean
        Dim startticks As Int64 = 0
        startticks = Now.Ticks()
        Dim resp As HttpWebResponse = Nothing
        If requestHttpWeb(setUrl(), resp, timeoutMS:=timeoutMS, content:=setContent()) Then
            _statusCode = resp.StatusCode
            _description = resp.StatusDescription
            resp.Close()
        Else
            raiseError(errorHandle.errorType.warning, _
                       "cannot connect to onlinecookie server, full url ", setUrl(), ", content ", setContent())
            setConnectionError()
        End If
        counter.increase(OCCONTEXT_SITESET_TIMEMS, ticksToMilliseconds(Now.Ticks() - startticks))

        Return success()
    End Function

    Public Sub New(ByVal site As String, ByVal userSiteRandomKey As String, ByVal user As String, _
                   Optional ByVal serverUrl As String = defaultServerUrl)
        _sharedCookies = New map(Of String, String)()
        _siteCookies = New map(Of String, String)()
        kickRight(serverUrl, network.pathSeperator)
        serverUrl += network.pathSeperator
        copy(_serverUrl, serverUrl)
        copy(_site, site)
        copy(_userSiteRandomKey, userSiteRandomKey)
        copy(_user, user)
    End Sub
End Class
